package com.zdkj.admin.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zdkj.admin.api.entity.SysFile;
import com.zdkj.admin.mapper.SysFileMapper;
import com.zdkj.admin.service.SysFileService;
import com.zdkj.common.core.constant.CommonConstants;
import com.zdkj.common.core.util.R;
import com.zdkj.common.minio.service.MinioTemplate;
import com.zdkj.common.security.util.SecurityUtils;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文件管理
 *
 * @author Luckly
 * @date 2019-06-18 17:18:42
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {
    private final MinioTemplate minioTemplate;
    private final String[] fileExtendNameLimit=new String[]{"jpeg","png","jpg","gif","doc","docx","xls","xlsx"};

    /**
     * 上传文件
     *
     * @param file
     * @return
     */
    @Override
    public R uploadFile(MultipartFile file) {
        String fileName = IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(file.getOriginalFilename());
        String fileExtendName = FileUtil.extName(file.getOriginalFilename());
        if(Arrays.asList(fileExtendNameLimit).contains(fileExtendName.toLowerCase())){
            Map<String, String> resultMap = new HashMap<>(4);
            resultMap.put("bucketName", CommonConstants.BUCKET_NAME);
            resultMap.put("fileName", fileName);
            resultMap.put("name", CommonConstants.BUCKET_NAME + "-" + fileName);
            resultMap.put("url", "/admin/sys-file/" + CommonConstants.BUCKET_NAME + "-" + fileName);

            try {
                minioTemplate.putObject(CommonConstants.BUCKET_NAME, fileName, file.getInputStream());
                //文件管理数据记录,收集管理追踪文件
                fileLog(file, fileName);
            } catch (Exception e) {
                log.error("上传失败", e);
                return R.failed(e.getLocalizedMessage());
            }
            return R.ok(resultMap);
        }else{

            return R.failed("选择的文件类型不允许上传");
        }
    }

    /**
     * 读取文件
     *
     * @param fileName
     * @param response
     */
    @Override
    public void getFile(String fileName, HttpServletResponse response) {
        int separator = fileName.lastIndexOf(StrUtil.DASHED);
        String name = fileName.substring(separator + 1);
        try (InputStream inputStream = minioTemplate.getObject(fileName.substring(0, separator),
                fileName.substring(separator + 1))) {
            SysFile file = this.getOne(Wrappers.<SysFile>query().lambda()
                    .eq(SysFile::getFileName, name));
            fileName = null != file ? file.getOriginal() : fileName;
            response.setContentType("application/octet-stream; charset=UTF-8");
            response.setHeader("content-disposition", "attachment;filename="
                    + URLEncoder.encode(fileName, "UTF-8"));
            IoUtil.copy(inputStream, response.getOutputStream());
        } catch (Exception e) {
            log.error("文件读取异常", e);
        }
    }

    @Override
    public void getFiles(List<String> fileNames, HttpServletResponse response) throws Exception {
        response.reset();
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename="
                + DateUtil.currentSeconds() + ".zip");
        ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
        BufferedOutputStream bos = new BufferedOutputStream(zos);

        for (String fileName : fileNames) {
            int separator = fileName.lastIndexOf(StrUtil.DASHED);
            String name = fileName.substring(separator + 1);
            try (InputStream inputStream = minioTemplate.getObject(fileName.substring(0, separator),
                    fileName.substring(separator + 1))) {
                SysFile file = this.getOne(Wrappers.<SysFile>query().lambda()
                        .eq(SysFile::getFileName, name));
                fileName = null != file ? file.getOriginal() : fileName;

                BufferedInputStream bis = new BufferedInputStream(inputStream);
                zos.putNextEntry(new ZipEntry(fileName));
                int len = 0;
                byte[] buf = new byte[10 * 1024];
                while ((len = bis.read(buf, 0, buf.length)) != -1) {
                    bos.write(buf, 0, len);
                }
                bis.close();
                bos.flush();

            } catch (Exception e) {
                log.error("文件读取异常", e);
            }
        }

        bos.close();

//        response.setContentType("application/octet-stream; charset=UTF-8");
//        response.setHeader("content-disposition", "attachment;filename="
//                + URLEncoder.encode(fileName, "UTF-8"));
//        IoUtil.copy(inputStream, response.getOutputStream());
    }


    /**
     * 删除文件
     *
     * @param id
     * @return
     */
    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteFile(Long id) {
        SysFile file = this.getById(id);
        minioTemplate.removeObject(CommonConstants.BUCKET_NAME, file.getFileName());
        return this.removeById(id);
    }

    /**
     * 文件管理数据记录,收集管理追踪文件
     *
     * @param file     上传文件格式
     * @param fileName 文件名
     */
    private void fileLog(MultipartFile file, String fileName) {
        SysFile sysFile = new SysFile();
        //原文件名
        String original = file.getOriginalFilename();
        sysFile.setFileName(fileName);
        sysFile.setOriginal(original);
        sysFile.setFileSize(file.getSize());
        sysFile.setType(FileUtil.extName(original));
        sysFile.setBucketName(CommonConstants.BUCKET_NAME);
        sysFile.setCreateUser(SecurityUtils.getUser().getUsername());
        this.save(sysFile);
    }

}
